﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using JF.RealtimeNotifications.Notifiers.Extensibility;
using Growl.Connector;
using Notification = JF.RealtimeNotifications.Notifiers.Extensibility.Notification;
using System.Threading.Tasks;
using System.Threading;

namespace JF.RealtimeNotifications.Notifiers.Growl
{
    public class GrowlNotifier : IPublisherNotifier
    {
        private Queue<Notification> notificationBuffer;
        private bool dequeueing;
        private int publishThrotellingTime = 2;
        private DateTime lastPublish;

        private GrowlConnector connector;
        private Application application;
        private bool isConnected;

        public String ApplicationName { get; set; }

        private List<Notification> notifications;
        public List<Notification> Notifications
        {
            get
            {
                if (notifications == null)
                {
                    notifications = new List<Notification>();
                }

                return notifications;
            }
        }

        public String Password { get; set; }
        public String Host { get; set; }
        public int Port { get; set; }

        public void Connect(OnConnectDelegate callback)
        {
            if (String.IsNullOrEmpty(this.ApplicationName))
            {
                throw new ArgumentNullException("ApplicationName");
            }

            if (this.isConnected)
            {
                throw new InvalidOperationException("Notifier already connected");
            }

            notificationBuffer = new Queue<Notification>();
            lastPublish = DateTime.MinValue;
            dequeueing = false;

            this.InitializeConnector();

            this.isConnected = true;

            if (callback != null)
            {
                callback(null);
            }
        }

        public void Publish(Notification notification)
        {
            if (!this.isConnected)
            {
                throw new InvalidOperationException("Notifier not connected");
            }

            notificationBuffer.Enqueue(notification);

            if (!dequeueing)
            {
                dequeueing = true;
                new Task(() =>
                {
                    while (notificationBuffer.Count > 0)
                    {
                        Notification queuedNotification; 
                        lock (notificationBuffer)
                        {
                            queuedNotification = notificationBuffer.Dequeue();                            
                        }

                        global::Growl.Connector.Notification growlNotification =
                                new global::Growl.Connector.Notification(
                                    application.Name,
                                    queuedNotification.Type,
                                    Guid.NewGuid().ToString(),
                                    queuedNotification.Title,
                                    queuedNotification.Text
                            );

                        connector.Notify(growlNotification);

                        Thread.Sleep(publishThrotellingTime * 1000);
                    }
                    dequeueing = false;
                }).Start();
            }
        }

        private void InitializeConnector()
        {
            if (String.IsNullOrEmpty(this.Password) && String.IsNullOrEmpty(this.Host))
            {
                this.connector = new GrowlConnector();
            }
            else
            {
                if (String.IsNullOrEmpty(Host))
                {
                    this.connector = new GrowlConnector(this.Password);
                }
                else
                {
                    this.connector = new GrowlConnector(this.Password, this.Host, this.Port);
                }
            }

            application = new Application(ApplicationName);

            var growlNotifications = new NotificationType[Notifications.Count];

            int notificationCounter = 0;
            foreach (var notification in Notifications)
            {
                growlNotifications[notificationCounter] = new NotificationType(notification.Type, notification.Title);
                notificationCounter++;
            }

            connector.Register(application, growlNotifications);
        }

    }
}
